﻿using AutoMapper;
using IOA.MES.Common;
using IOA.MES.DataAccess.Entity;
using IOA.MES.DataAccess.Enum;
using IOA.MES.DataAccess.VModel;
using IOA.MES.Log.Business;
using IOA.MES.Log.DataAccess.VModel;
using IOA.Utils;
using NLog;
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Threading.Tasks;

namespace IOA.MES.Business
{
    /// <summary>
    /// 降级复判管理（错误代码：138001）
    /// </summary>
    public class DowngradeMgr
    {
        private MESContext mesDb = new MESContext("MES");
        private Logger logger = LogManager.GetCurrentClassLogger();

        /// <summary>
        /// 组件降级
        /// </summary>
        /// <param name="barCodes">条码列表</param>
        /// <param name="badCodes">异常代码列表</param>
        /// <param name="curSite">降级站点</param>
        /// <param name="qLevel">等级</param>
        /// <param name="downgradeBy">降级人</param>
        public void Downgrade(List<string> barCodes, List<string> badCodes, SiteType curSite, string qLevel, int downgradeBy)
        {
            barCodes = barCodes.Where(_ => !string.IsNullOrWhiteSpace(_)).Select(_ => _.ToUpper().Trim()).Distinct().ToList();
            badCodes = badCodes.Where(_ => !string.IsNullOrWhiteSpace(_)).Select(_ => _.ToUpper().Trim()).Distinct().ToList();
            if (!barCodes.Any())
            {
                return;
            }

            var components = mesDb.Components.AsNoTracking().Where(_ => barCodes.Contains(_.BarCode) && _.Valid).ToList();
            if (components.Any(_ => !string.IsNullOrWhiteSpace(_.PackNo)))
            {
                throw new MultiLanBizException(138006, string.Join("、", components.Where(_ => !string.IsNullOrWhiteSpace(_.PackNo)).Select(_ => _.BarCode)));//下列组件已被打包，请解包后再试
            }
            components.ForEach(_ => _.BarCode = _.BarCode.ToUpper().Trim());
            var entities = new List<Downgrade>();

            var productBadReasons = new List<ProductViaSiteBadReason>();
            if (barCodes.Any(_ => !components.Select(c => c.BarCode).Contains(_)))
            {
                throw new MultiLanBizException(138001, string.Join("、", barCodes.Where(_ => !components.Select(c => c.BarCode.ToUpper().Trim()).Contains(_))));//当前组件条码不存在
            }

            var existDowngrades = mesDb.Downgrades.AsNoTracking().Where(_ => barCodes.Contains(_.BarCode) && _.Reviewed == false && _.Valid).ToList();
            if (!qLevel.Equals("Q1", StringComparison.CurrentCultureIgnoreCase) && existDowngrades.Any())
            {
                throw new MultiLanBizException(138004, string.Join("、", existDowngrades.Select(_ => _.BarCode)));//该组件存在未复判记录
            }

            #region 匹配异常代码
            badCodes = badCodes.Select(_ => _.ToUpper().Trim()).Distinct().ToList();
            var total = 0;
            var badReasons = new BadReasonMgr().QueryReasons(1, 0, out total, string.Empty, string.Empty).Where(_ => badCodes.Contains(_.ReasonCode, StringComparer.CurrentCultureIgnoreCase)).ToList();

            foreach (var barCode in barCodes)
            {
                entities.Add(new Downgrade
                {
                    DowngradeBadTypeCode = string.Join(",", badReasons.Select(_ => _.BadTypeCode).Distinct()),
                    DowngradeBadTypeName = string.Join(",", badReasons.Select(_ => _.BadTypeName).Distinct()),
                    DowngradeBadCode = string.Join(",", badReasons.Select(_ => _.ReasonCode).Distinct()),
                    DowngradeBadName = string.Join(",", badReasons.Select(_ => _.ReasonCN).Distinct()),
                    OrderNo = components.FirstOrDefault(_ => StringUtils.Equals(_.BarCode, barCode)).OrderNo,
                    WorkOrderNo = components.FirstOrDefault(_ => StringUtils.Equals(_.BarCode, barCode)).WorkOrderNo,
                    ProductCode = components.FirstOrDefault(_ => StringUtils.Equals(_.BarCode, barCode)).ProductCode,
                    ProductName = components.FirstOrDefault(_ => StringUtils.Equals(_.BarCode, barCode)).ProductName,
                    ProductSpecs = components.FirstOrDefault(_ => StringUtils.Equals(_.BarCode, barCode)).ProductSpecs,
                    WorkshopCode = components.FirstOrDefault(_ => StringUtils.Equals(_.BarCode, barCode)).WorkshopCode,
                    WorkshopName = components.FirstOrDefault(_ => StringUtils.Equals(_.BarCode, barCode)).WorkshopName,
                    BarCode = barCode,
                    CurSite = curSite,
                    QLevel = qLevel,
                    CreateBy = downgradeBy,
                    CreateTime = DateTime.Now,
                });
                productBadReasons.AddRange(badReasons.Select(_ => new ProductViaSiteBadReason
                {
                    JudgeType = JudgeType.Downgrade,
                    BarCode = barCode,
                    BadTypeCode = _.BadTypeCode,
                    BadReasonCode = _.ReasonCode,
                    CreateBy = downgradeBy,
                    CreateTime = DateTime.Now
                }));
            }
            #endregion

            mesDb.Downgrades.AddRange(entities);
            mesDb.SaveChanges();

            productBadReasons.ForEach(_ => _.JudgeId = entities.FirstOrDefault(d => string.Equals(_.BarCode, d.BarCode, StringComparison.CurrentCultureIgnoreCase)).PkId);
            mesDb.ProductViaSiteBadReasons.AddRange(productBadReasons);
            mesDb.SaveChanges();

            foreach (var item in components)
            {
                var model = entities.FirstOrDefault(_ => StringUtils.Equals(_.BarCode, item.BarCode));
                var sql = $"UPDATE Component SET ProductStatus = @ProductStatus, " +
                $"QLevel = @QLevel, " +
                $"DowngradeTypeCode = @DowngradeTypeCode, " +
                $"DowngradeTypeName = @DowngradeTypeName, " +
                $"DowngradeCode = @DowngradeCode, " +
                $"DowngradeName = @DowngradeName, " +
                $"ScrapTime = @ScrapTime, " +
                $"ModifyBy = @ModifyBy, " +
                $"ModifyTime = @ModifyTime " +
                $"WHERE BarCode = @BarCode;";
                var sqlParams = new List<SqlParameter>
                {
                    new SqlParameter("@ProductStatus",(int)( StringUtils.Equals(model.QLevel, "BF") ? ProductStatus.Scrapped : (StringUtils.Equals(model.QLevel, "Q1") ? ProductStatus.Normal : ProductStatus.Downgrade))),
                    new SqlParameter("@QLevel", model.QLevel),
                    new SqlParameter("@DowngradeTypeCode", model.DowngradeBadTypeCode),
                    new SqlParameter("@DowngradeTypeName", model.DowngradeBadTypeName),
                    new SqlParameter("@DowngradeCode",  model.DowngradeBadCode),
                    new SqlParameter("@DowngradeName", model.DowngradeBadName),
                    new SqlParameter("@ScrapTime", StringUtils.Equals(model.QLevel, "Q1") ? (DateTime?)null : DateTime.Now),
                    new SqlParameter("@ModifyBy", model.CreateBy),
                    new SqlParameter("@ModifyTime", DateTime.Now),
                    new SqlParameter("@BarCode", item.BarCode),
                };
                mesDb.Database.ExecuteSqlCommand(sql, sqlParams.ToArray());
            }

            new TaskFactory().StartNew(() =>
            {
                try
                {
                    var user = new UserMgr().QueryUsers(new List<int> { entities.FirstOrDefault().CreateBy }).FirstOrDefault();
                    var logs = entities.Select(model => new VOprLog
                    {
                        SubjectType = "组件",
                        SubjectNo = model.BarCode,
                        LogName = StringUtils.Equals(model.QLevel, "BF") ? "组件报废" : (StringUtils.Equals(model.QLevel, "Q1") ? "等级恢复" : "组件降级"),
                        LogStatus = StringUtils.Equals(model.QLevel, "Q1") ? "正常" : "异常",
                        OpeatorId = model.CreateBy,
                        OpeatorName = user?.TrueName,
                        LogContent = StringUtils.Equals(model.QLevel, "BF") ? "组件报废" : (StringUtils.Equals(model.QLevel, "Q1") ? "等级恢复：Q1" : $"组件降级：{model.QLevel}"),
                        LogTime = DateTime.Now,
                        CreateBy = model.CreateBy
                    }).ToList();
                    new OprLogMgr().AddLogs(logs);
                }
                catch (Exception ex)
                {
                    logger.Fatal(ex, $"组件降级异常，组件条码：{string.Join(",", components.Select(_ => _.BarCode).ToList())}：{ex.Message}");
                }
            });
        }

        /// <summary>
        /// 获取降级记录
        /// </summary>
        public VDowngrade GetDowngrade(string barCode)
        {
            var entity = mesDb.Downgrades.AsNoTracking().Where(_ => _.BarCode == barCode && _.Valid).OrderByDescending(_ => _.PkId).FirstOrDefault();
            return Mapper.Map<VDowngrade>(entity);
        }

        /// <summary>
        /// 复判组件
        /// </summary>
        /// <param name="model">降级对象</param>
        public void Review(VDowngrade model)
        {
            var entity = model.PkId > 0 ? mesDb.Downgrades.AsNoTracking().FirstOrDefault(_ => _.PkId == model.PkId && _.Valid)
                : mesDb.Downgrades.AsNoTracking().FirstOrDefault(_ => _.BarCode == model.BarCode && _.Reviewed == false && _.Valid);
            if (entity == null)
            {
                throw new MultiLanBizException(138005);//该组件不存在未复判的记录
            }

            var component = mesDb.Components.AsNoTracking().FirstOrDefault(_ => _.BarCode == entity.BarCode && _.Valid);
            if (component == null)
            {
                throw new MultiLanBizException(138001);//当前组件条码不存在
            }

            var reasons = string.IsNullOrWhiteSpace(model.ReviewBadCode) ? new List<VBadReason>()
                : new BadReasonMgr().QueryReasons(model.ReviewBadCode.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
                    .Select(_ => _.ToUpper().Trim()).Distinct().ToList());

            mesDb.Downgrades.Attach(entity);
            entity.Reviewed = true;
            entity.ReviewLevel = model.ReviewLevel;
            entity.ReviewedBy = model.ReviewedBy;
            entity.ReviewTime = DateTime.Now;
            entity.ReviewBadTypeCode = string.Join(",", reasons.Select(_ => _.BadTypeCode).Distinct());
            entity.ReviewBadTypeName = string.Join(",", reasons.Select(_ => _.BadTypeName).Distinct());
            entity.ReviewBadCode = string.Join(",", reasons.Select(_ => _.ReasonCode).Distinct());
            entity.ReviewBadName = string.Join(",", reasons.Select(_ => $"{_.ReasonCN}({_.ReasonVN})").Distinct());
            entity.ModifyBy = model.ModifyBy;
            entity.ModifyTime = DateTime.Now;
            mesDb.SaveChanges();

            new TaskFactory().StartNew(() =>
            {
                try
                {
                    mesDb.Components.Attach(component);
                    if (reasons.Any())
                    {
                        var badReasons = reasons.Select(_ => new ProductViaSiteBadReason
                        {
                            JudgeType = JudgeType.Downgrade,
                            JudgeId = entity.PkId,
                            BarCode = model.BarCode,
                            BadTypeCode = _.BadTypeCode,
                            BadReasonCode = _.ReasonCode,
                            CreateBy = model.CreateBy,
                            CreateTime = DateTime.Now
                        }).ToList();
                        mesDb.ProductViaSiteBadReasons.AddRange(badReasons);
                    }

                    component.ProductStatus = StringUtils.Equals(entity.ReviewLevel, "BF") ? ProductStatus.Scrapped : (StringUtils.Equals(entity.ReviewLevel, "Q1") ? ProductStatus.Normal : ProductStatus.Downgrade);
                    component.QLevel = entity.ReviewLevel;
                    component.DowngradeCode = model.ReviewBadCode;
                    component.DowngradeName = model.ReviewBadName;
                    component.DowngradeTypeCode = model.ReviewBadTypeCode;
                    component.DowngradeTypeName = model.ReviewBadTypeName;
                    component.ScrapTime = StringUtils.Equals(entity.ReviewLevel, "Q1") ? null : component.ScrapTime;
                    component.ModifyBy = model.ModifyBy;
                    component.ModifyTime = DateTime.Now;

                    component.Locked = false;
                    component.LockedTime = null;
                    component.LockedBy = 0;
                    component.LockedReason = string.Empty;

                    var lockEntities = mesDb.LockRecords.AsNoTracking().Where(_ => _.BarCode == component.BarCode && _.UnLockedBy <= 0 && _.Valid).ToList();
                    foreach (var lockEntity in lockEntities)
                    {
                        mesDb.LockRecords.Attach(lockEntity);
                        lockEntity.UnLockedBy = component.ModifyBy;
                        lockEntity.UnLockedReason = "复判解锁";
                        lockEntity.UnLockedTime = DateTime.Now;
                        lockEntity.ModifyBy = component.ModifyBy;
                        lockEntity.ModifyTime = DateTime.Now;
                    }
                    mesDb.SaveChanges();

                    model = Mapper.Map<VDowngrade>(entity);
                    var user = new UserMgr().QueryUsers(new List<int> { model.ModifyBy }).FirstOrDefault();
                    var log = new VOprLog
                    {
                        SubjectType = "组件",
                        SubjectNo = component.BarCode,
                        LogName = "组件复判",
                        LogStatus = StringUtils.Equals(entity.ReviewLevel, "Q1") ? "正常" : "异常",
                        OpeatorId = model.CreateBy,
                        OpeatorName = user?.TrueName,
                        LogContent = $"组件复判结果：{model.ReviewLevel}，原等级：{model.QLevel}",
                        LogTime = DateTime.Now,
                        CreateBy = model.CreateBy
                    };
                    new OprLogMgr().AddLogs(new List<VOprLog> { log });
                }
                catch (Exception ex)
                {
                    logger.Fatal(ex, $"复判组件异常，组件条码：{component.BarCode}，异常原因：{ex.Message}");
                }
            });
        }

        /// <summary>
        /// 获取降级对象
        /// </summary>
        /// <param name="downgradeId">降级ID</param>
        public VDowngrade GetDowngrade(int downgradeId)
        {
            var entity = mesDb.Downgrades.AsNoTracking().FirstOrDefault(_ => _.PkId == downgradeId && _.Valid);
            var model = Mapper.Map<VDowngrade>(entity);

            if (model != null)
            {
                var userIds = new List<int> { model.CreateBy, model.ReviewedBy }.Where(_ => _ > 0).Distinct().ToList();
                var users = !userIds.Any() ? new List<VUser>() : new UserMgr().QueryUsers(userIds);
                model.CNCreateBy = users.FirstOrDefault(_ => _.PkId == model.CreateBy)?.TrueName ?? string.Empty;
                model.CNReviewedBy = users.FirstOrDefault(_ => _.PkId == model.ReviewedBy)?.TrueName ?? string.Empty;
            }

            return model;
        }

        /// <summary>
        /// 查询降级列表
        /// </summary>
        /// <param name="page">页面索引</param>
        /// <param name="limit">页面容量</param>
        /// <param name="total">总数量</param>
        /// <param name="workshopCode">车间代码</param>
        /// <param name="orderNo">订单号</param>
        /// <param name="workOrderNo">工单号</param>
        /// <param name="barCode">条码</param>
        /// <param name="startTime">开始创建时间</param>
        /// <param name="endTime">截止创建时间</param>
        /// <param name="reviewed">是否复判</param>
        /// <param name="result">复判结果</param>
        /// <returns>降级列表</returns>
        public List<VDowngrade> QueryDowngrades(int page, int limit, out int total, string workshopCode, string orderNo, string workOrderNo
            , string barCode, DateTime? startTime, DateTime? endTime, bool? reviewed)
        {
            var query = mesDb.Downgrades.AsNoTracking().Where(_ => _.Valid);
            if (!string.IsNullOrWhiteSpace(barCode))
            {
                query = query.Where(_ => _.BarCode == barCode);
            }
            else
            {
                if (!string.IsNullOrWhiteSpace(workshopCode))
                {
                    query = query.Where(_ => _.WorkshopCode == workshopCode);
                }
                if (!string.IsNullOrWhiteSpace(orderNo))
                {
                    query = query.Where(_ => _.OrderNo == orderNo);
                }
                if (!string.IsNullOrWhiteSpace(workOrderNo))
                {
                    query = query.Where(_ => _.WorkOrderNo == workOrderNo);
                }

                if (startTime.HasValue)
                {
                    query = query.Where(_ => _.CreateTime >= startTime.Value);
                }
                if (endTime.HasValue)
                {
                    query = query.Where(_ => _.CreateTime <= endTime.Value);
                }
                if (reviewed.HasValue)
                {
                    query = query.Where(_ => _.Reviewed == reviewed.Value);
                }
            }

            total = query.Count();
            query = query.OrderByDescending(_ => _.PkId);

            var entities = limit <= 0 ? query.ToList() : query.Skip((page - 1) * limit).Take(limit).ToList();
            var models = Mapper.Map<List<VDowngrade>>(entities);

            if (models.Any())
            {
                var userIds = models.Select(_ => _.CreateBy).ToList();
                userIds.AddRange(models.Select(_ => _.ReviewedBy));
                var users = new UserMgr().QueryUsers(userIds.Where(_ => _ > 0).Distinct().ToList());

                models.ForEach(model =>
                {
                    model.CNCreateBy = users.FirstOrDefault(_ => _.PkId == model.CreateBy)?.TrueName ?? string.Empty;
                    model.CNReviewedBy = users.FirstOrDefault(_ => _.PkId == model.ReviewedBy)?.TrueName ?? string.Empty;
                });
            }

            return models;
        }


        public List<VDowngrade> QueryDowngrades(string workshopCode, string orderNo, string workOrderNo
            , string barCode, DateTime? startTime, DateTime? endTime, bool? reviewed)
        {
            var query = mesDb.Downgrades.AsNoTracking().Where(_ => _.Valid);
            if (!string.IsNullOrWhiteSpace(barCode))
            {
                query = query.Where(_ => _.BarCode == barCode);
            }
            else
            {
                if (!string.IsNullOrWhiteSpace(workshopCode))
                {
                    query = query.Where(_ => _.WorkshopCode == workshopCode);
                }
                if (!string.IsNullOrWhiteSpace(orderNo))
                {
                    query = query.Where(_ => _.OrderNo == orderNo);
                }
                if (!string.IsNullOrWhiteSpace(workOrderNo))
                {
                    query = query.Where(_ => _.WorkOrderNo == workOrderNo);
                }

                if (startTime.HasValue)
                {
                    query = query.Where(_ => _.CreateTime >= startTime.Value);
                }
                if (endTime.HasValue)
                {
                    query = query.Where(_ => _.CreateTime <= endTime.Value);
                }
                if (reviewed.HasValue)
                {
                    query = query.Where(_ => _.Reviewed == reviewed.Value);
                }
            }

            query = query.OrderByDescending(_ => _.PkId);

            var entities = query.ToList();
            var models = Mapper.Map<List<VDowngrade>>(entities);

            if (models.Any())
            {
                var userIds = models.Select(_ => _.CreateBy).ToList();
                userIds.AddRange(models.Select(_ => _.ReviewedBy));
                var users = new UserMgr().QueryUsers(userIds.Where(_ => _ > 0).Distinct().ToList());

                models.ForEach(model =>
                {
                    model.CNCreateBy = users.FirstOrDefault(_ => _.PkId == model.CreateBy)?.TrueName ?? string.Empty;
                    model.CNReviewedBy = users.FirstOrDefault(_ => _.PkId == model.ReviewedBy)?.TrueName ?? string.Empty;
                });
            }

            return models;
        }

    }
}
